home *** CD-ROM | disk | FTP | other *** search
- // -------------------------------------------------------------------------------------
- // PaletteMatrix.m
- // -------------------------------------------------------------------------------------
- // Permission is granted to freely redistribute this source code, and to use fragments
- // of this code in your own applications if you find them to be useful. This class,
- // along with the source code, come with no warranty of any kind, and the user assumes
- // all responsibility for its use.
- // -------------------------------------------------------------------------------------
-
- extern "Objective-C" {
- #import <objc/objc.h>
- #import <appkit/appkit.h>
- #import <libc.h>
- #import <stdlib.h>
- #import <stdio.h>
- #import <string.h>
- #import <mach/mach.h>
- #import <dpsclient/wraps.h>
- #import <math.h>
- }
-
- #import "ImagePortfolio.h"
- #import "ExpandImage.h"
- #import "Portfolio.h"
- #import "PaletteCell.h"
- #import "PaletteMatrix.h"
-
- // -------------------------------------------------------------------------------------
- // misc constants/macros
- #define prefVERSION 1.02
- #define CELLCLASS [PaletteCell class]
- #define newCELL [protoCell copy]
- #define VSPACER 2.0
- #define freeLIST(L) { if (L) { [[L freeObjects] free]; L = (id)nil; } }
- #define dbgMARKER(X) { printf("%s; %d...\n", sel_getName(_cmd), X); }
- #define cpNIL (char*)nil
-
- // -------------------------------------------------------------------------------------
- // origin/size shortcuts
- #define X origin.x
- #define Y origin.y
- #define W size.width
- #define H size.height
-
- // -------------------------------------------------------------------------------------
- static id paletteCellClass = (id)nil;
- static char *cutListPaths = cpNIL;
- static int sigPasteboard = -1;
- static char *imagePathType = cpNIL;
- static id pasteFont = (id)nil;
-
- // -------------------------------------------------------------------------------------
- // static selectors
- extern "Objective-C" {
- static SEL compareCellTitle = @selector(compareCellTitle::);
- static SEL _updateCells = @selector(_updateCells);
- }
-
- // -------------------------------------------------------------------------------------
- @implementation PaletteMatrix
-
- // -------------------------------------------------------------------------------------
-
- /* new instance */
- + newFrame:(const NXRect *)fRect
- {
- return [[self allocFromZone:NXDefaultMallocZone()] initFrame:fRect];
- }
-
- /* initialize instance */
- - initFrame:(const NXRect*)fRect
- {
-
- /* init globals */
- if (!imagePathType) imagePathType = NXCopyStringBuffer("imagePathType");
-
- /* init super */
- [super initFrame:fRect];
- if (!paletteCellClass) paletteCellClass = CELLCLASS;
-
- /* init vars */
- delegate = self;
- pbCopyTiff = YES;
- pbCopyPath = NO;
- loadingCells = NO;
- loadMutex = mutex_alloc();
-
- /* image list container */
- imageList = [[[List alloc] initCount:1] empty];
- deleteList = (id)nil;
-
- /* default protocell */
- protoCell = [paletteCellClass new];
- intercell.width = VSPACER;
- intercell.height = VSPACER;
-
- /* size */
- [self setMode:NX_LISTMODE];
- [self setEmptySelectionEnabled:YES];
- [self setAutoscroll:YES];
- [self setBackgroundGray:NX_LTGRAY];
- [self setCellBackgroundGray:NX_LTGRAY];
- [self setFlipped:YES];
-
- return self;
- }
-
- /* free object */
- - free
- {
- [imageList free]; // cells are freed by super
- freeLIST(deleteList);
- mutex_free(loadMutex);
- return [super free];
- }
-
- /* free all cells */
- - freeCells
- {
- [self clearSelectedCell];
- [self renewRows:0 cols:0];
- [cellList freeObjects];
- [imageList empty];
- [self sizeToCells];
- freeLIST(deleteList);
- [window setDocEdited:YES];
- return self;
- }
-
- /* remove selected cells (return deleted list) */
- - freeSelectedCells
- {
- int i;
- id iCell, dList = [self selectedCellList];
-
- /* return if no selected cells */
- if (!dList) return (id)nil;
-
- /* delete selected cells from matrix */
- i = [dList count];
- [self clearSelectedCell];
- [self renewRows:0 cols:0];
- mutex_lock(loadMutex);
- while(i) {
- iCell = [dList objectAt:--i];
- [imageList removeObject:iCell];
- [cellList removeObject:iCell];
- [iCell setDelegate:(id)nil];
- }
- mutex_unlock(loadMutex);
- [self sizeToCells];
- [window setDocEdited:YES];
-
- return dList;
- }
-
- // -------------------------------------------------------------------------------------
- // set attributes
-
- /* set delegate */
- - setDelegate:anObject
- {
- delegate = anObject;
- return self;
- }
-
- /* set to font */
- - setFont:fontObj
- {
- int i = [self cellCount];
- [super setFont:fontObj];
- [window disableDisplay];
- while(i) [[cellList objectAt:--i] setFont:fontObj];
- [protoCell setFont:fontObj];
- conFlags.calcSize = YES;
- [window reenableDisplay];
- return self;
- }
-
- - changeFont:sender
- {
- [self setFont:[[FontManager new] convertFont:[self font]]];
- return self;
- }
-
- /* set preferences (THIS ASSUMES PROPER PREFERENCE BUFFER FORMAT) */
- - (BOOL)setPreferences:(char*)prefBuff returnRows:(int*)rows cols:(int*)cols
- {
- NXSize size;
- float ver, pointSize;
- char fontName[256];
- id o;
- if (!prefBuff || !*prefBuff) return NO;
- sscanf(prefBuff, "%f %f %f %d %d %s %f", &ver,
- &size.width, &size.height, cols, rows, fontName, &pointSize);
- [self setCellSize:&size];
- if (o = [Font newFont:fontName size:pointSize]) [self setFont:o];
- return YES;
- }
-
- // -------------------------------------------------------------------------------------
- // pasteboard
-
- - (char*)imagePaths:list
- {
- int i, c, len = 0;
- char *fullPath, *p;
- if (!(c = [list count])) return cpNIL;
- for (i=0;i<c;i++) { len += strlen([[list objectAt:i] imagePath]) + 1; }
- fullPath = p = (char*)malloc(len + 1);
- for (i=0;i<c;i++) { sprintf(p, "%s\t", [[list objectAt:i] imagePath]); p += strlen(p); }
- *(p - 1) = 0; // remove trailing tab (p > fullPath is guaranteed)
- return fullPath;
- }
-
- /* copy listed cells to pasteboard */
- - copyToPasteboard:list paths:(char*)listPaths
- {
- char *dt[2], *data, *path;
- int len, max, n = 0;
- NXStream *stream;
- NXImage *image;
- Pasteboard *pb;
- id first;
-
- /* return if no items to copy */
- if (![list count]) return self;
-
- /* return if no selected cell */
- pb = [Pasteboard newName:NXGeneralPboard];
- if (!pb) { NXLogError("ImagePortfolio: no pasteboard"); return self; }
-
- /* declare pasteboard types */
- if (pbCopyPath) dt[n++] = (char*)NXAsciiPboardType;
- if (pbCopyTiff) dt[n++] = (char*)NXTIFFPboardType;
- dt[n++] = (char*)imagePathType;
- [pb declareTypes:dt num:n owner:NXApp];
- sigPasteboard = [pb changeCount];
-
- /* get first image */
- first = [list objectAt:0];
-
- /* write image representation to pasteboard */
- if (pbCopyTiff) {
- stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
- image = [first image];
- [image setName:[first cellTitle]];
- [image lockFocus];
- [image writeTIFF:stream];
- [image unlockFocus];
- NXGetMemoryBuffer(stream, &data, &len, &max);
- [pb writeType:NXTIFFPboardType data:data length:len];
- NXClose(stream);
- NXFreeObjectBuffer(data, len);
- }
-
- /* write image path name to pasteboard */
- if (pbCopyPath && (path = (char*)[first imagePath])) {
- [pb writeType:NXAsciiPboardType data:path length:strlen(path) + 1];
- }
-
- /* write custom type */
- if (listPaths) [pb writeType:imagePathType data:listPaths length:strlen(listPaths) + 1];
-
- return self;
- }
-
- /* copy TIFF from pasteboard */
- - copyFromPasteboard
- {
- id pb, pSave, imageId = (id)nil;
- char **types, titleBuff[256], *tiffName = cpNIL;
- char *path = (char*)[NXApp lastPath];
- NXStream *stream;
-
- /* return if no pasteboard */
- if (!(pb = [Pasteboard newName:NXGeneralPboard])) return (id)nil;
-
- /* check for custom type */
- for (types = (char**)[pb types]; *types && strcmp(*types,imagePathType); types++);
- if (*types) {
- char *data;
- int len;
- if (![pb readType:imagePathType data:&data length:&len]) return (id)nil;
- [delegate loadFileString:data :NO :NO];
- [pb deallocatePasteboardData:data length:len];
- return (id)nil;
- }
-
- /* search pasteboard types for a TIFF file */
- for (types=(char**)[pb types]; *types && strcmp(*types,(char*)NXTIFFPboardType); types++);
- if (!*types) return (id)nil;
-
- /* read data from pasteboard */
- if ((stream = [pb readTypeToStream:NXTIFFPboardType]) &&
- (imageId = [[NXImage alloc] initFromStream:stream]) &&
- ([imageId lastRepresentation]) ) {
-
- /* display image */
- sprintf(titleBuff, "%s - TIFF", ([imageId name]?[imageId name]:"NXImage"));
- [[delegate expandImage] showImage:imageId:(char*)nil title:titleBuff];
-
- /* display save panel */
- pSave = [SavePanel new];
- [pSave setTitle:"Save TIFF file image"];
- [pSave setPrompt:"File:"];
- [pSave setRequiredFileType:"tiff"];
- [pSave setDirectory:path];
- if ([pSave runModalForDirectory:path file:""]) tiffName = (char*)[pSave filename];
-
- /* save to file */
- if (tiffName) {
- NXSaveToFile(stream, tiffName);
- [delegate loadFileString:tiffName :NO :NO];
- }
-
- /* free memory */
- NXCloseMemory(stream, NX_FREEBUFFER);
-
- } else {
- const char *e, *m, *c;
-
- /* show warning/error panel */
- e = NXLocalizedString("Error", cpNIL, cpNIL);
- m = NXLocalizedString("Unable to read TIFF from PasteBoard.", cpNIL, cpNIL);
- c = NXLocalizedString("Continue", cpNIL, cpNIL);
- NXRunAlertPanel(e, m, c, cpNIL, cpNIL);
-
- }
-
- /* free image */
- [[delegate expandImage] clearImage];
- if (imageId) [imageId free];
-
- return self;
- }
-
- // -------------------------------------------------------------------------------------
- // first responder
- #define delKEY 0x7F
-
- /* never relinquish firstResponder */
- - resignFirstResponder { return (id)nil; }
-
- /* show preferences (menu connection should point here to allow disabling when necessary) */
- - showPreferences:sender { return [NXApp showPreferences:self]; }
-
- /* perform close window functions */
- - performClose:sender { return [window performClose:sender]; }
- - performMiniaturize:sender { return [window performMiniaturize:sender]; }
-
- /* accept keydown event (check for delete key) */
- - keyDown:(NXEvent*)e
- {
- if (e->data.key.charCode == delKEY) return [self delete:(id)nil];
- return [super keyDown:e];
- }
-
- /* delete selected cells */
- - delete:sender
- {
- freeLIST(deleteList);
- deleteList = [self freeSelectedCells];
- [self display];
- return self;
- }
-
- /* copy previously deleted cells back to imageList */
- - undelete:sender
- {
- int i, c;
- id iCell;
- if (!deleteList) return self;
- for (c = [deleteList count], i = 0; i < c; i++) {
- iCell = [deleteList objectAt:i];
- if ([self findCellWithImageFilePath:(char*)[iCell imagePath]]) continue;
- [iCell setDelegate:delegate];
- [self addImageCell:iCell];
- [window setDocEdited:YES];
- }
- [deleteList free];
- deleteList = (id)nil;
- [self resizeAndDisplay];
- return self;
- }
-
- /* return true if undeletable */
- - (BOOL)undeletable
- {
- return ([deleteList count])? YES : NO;
- }
-
- /* cut selected cells */
- - cut:sender
- {
- [self delete:sender];
- if (!deleteList) return self;
- if (cutListPaths) { free(cutListPaths); cutListPaths = cpNIL; }
- cutListPaths = [self imagePaths:deleteList];
- [self copyToPasteboard:deleteList paths:cutListPaths];
- return self;
- }
-
- /* make a copy of the selected cells */
- - copy:sender
- {
- id dList = [self selectedCellList];
- if (!dList) return self;
- if (cutListPaths) { free(cutListPaths); cutListPaths = cpNIL; }
- cutListPaths = [self imagePaths:dList];
- [self copyToPasteboard:dList paths:cutListPaths];
- [dList free];
- return self;
- }
-
- /* copy cut cells back to imageList */
- - paste:sender
- {
- if ([[Pasteboard new] changeCount] != sigPasteboard) [self copyFromPasteboard];
- else [delegate loadFileString:cutListPaths :NO :NO];
- [self resizeAndDisplay];
- return self;
- }
-
- /* select all cells */
- - selectAll:sender
- {
- int i;
- if (mFlags.radioMode) return self;
- i = [self imageListCount];
- while(i) [[self imageAt:--i] setState:1];
- [self display];
- return self;
- }
-
- /* sort images by name */
- - sortByCellTitle:sender
- {
- [self shellSort:compareCellTitle];
- [self resizeAndDisplay];
- return self;
- }
-
- /* copy font */
- - copyFont:sender
- {
- pasteFont = [self font];
- return self;
- }
-
- /* paste font */
- - pasteFont:sender
- {
- if (pasteFont) [self setFont:pasteFont];
- return self;
- }
-
- // -------------------------------------------------------------------------------------
- // first responder: save
-
- - (char*)getPreferenceString:(char*)buff
- {
- int rows, cols;
- [delegate getDisplayedRows:&rows cols:&cols];
- sprintf(buff, "%.2f %.0f %.0f %d %d %s %.0f", prefVERSION,
- cellSize.width, cellSize.height, cols, rows, [font name], [font pointSize]);
- return buff;
- }
-
- /* save file names */
- - save:sender
- {
- if ([self imageListCount]) return [delegate save:sender];
- return self;
- }
- - saveAs:sender
- {
- if ([self imageListCount]) return [delegate saveAs:sender];
- return self;
- }
-
- /* save to file (called by delegate) */
- - saveToFile:(char*)fileName
- {
- int i, c = [self imageListCount];
- char buff[512];
- FILE *fNum;
-
- /* get file name to save */
- if (!fileName || !c) return (id)nil;
-
- /* open file */
- fNum = fopen(fileName, "w");
- if (!fNum) return (id)nil;
-
- /* write preferences and path names */
- fprintf(fNum, ": %s\n", [self getPreferenceString:buff]);
- for (i = 0; i < c; i++) fprintf(fNum, "%s\n", [[self imageAt:i] imagePath]);
-
- /* close file and indicate that window has been saved */
- fclose(fNum);
- [window setDocEdited:NO];
-
- return self;
- }
-
- // -------------------------------------------------------------------------------------
- // size
-
- /* set intercell size */
- - setIntercell:(const NXSize*)aSize
- {
- [super setIntercell:aSize];
- [self sizeToCells];
- return self;
- }
-
- /* set image size */
- - setCellSize:(const NXSize*)size
- {
- int i = [self cellCount];
-
- /* update cell size */
- [super setCellSize:size];
-
- /* notify cells of size change */
- while(i) [[cellList objectAt:--i] setCellSize:size];
- [protoCell setCellSize:size];
-
- /* recalc sizes */
- [self sizeToCells];
-
- return self;
- }
-
- /* readjust matrix to fit cells */
- - sizeToCells
- {
- int i, rows, cols, cnt;
- id oldCell;
-
- /* clear selected cells */
- [self clearSelectedCell];
-
- /* adjust rows/cols */
- cnt = [self imageListCount];
- cols = (int)((frame.size.width + intercell.width) / (cellSize.width + intercell.width));
- if (cols < 1) cols = 1;
- rows = (cnt + cols - 1) / cols;
- if (rows < 1) rows = 1;
- [self renewRows:rows cols:cols];
-
- /* replace cells (and free old cells) */
- for (i = 0; i < cnt; i++) {
- oldCell = [cellList replaceObjectAt:i with:[self imageAt:i]];
- if (oldCell && ![oldCell image]) [oldCell free];
- }
-
- /* now resize matrix */
- [super sizeToCells];
-
- return self;
- }
-
- /* resize matrix and redisplay */
- - resizeAndDisplay
- {
- [self sizeToCells];
- [self display];
- return self;
- }
-
- /* return cellSize */
- - (NXSize*)cellSize
- {
- return &cellSize;
- }
-
- /* return intercellSize */
- - (NXSize*)intercell
- {
- return &intercell;
- }
-
- // -------------------------------------------------------------------------------------
- // add cell methods
-
- /* called by main thread when cell was added */
- - _updateCells
- {
- [self resizeAndDisplay];
- loadingCells = NO;
- return self;
- }
-
- /* add new image */
- - addImage:(const char*)filePath
- {
- id iCell;
-
- /* see if its already in the list */
- if ([self findCellWithImageFilePath:(char*)filePath]) return (id)nil;
-
- /* add and show cell */
- iCell = newCELL;
- if (![iCell setImageFile:filePath]) { [iCell free]; return (id)nil; }
- [iCell setDelegate:delegate];
- [self addImageCell:iCell];
- if (!loadingCells) {
- loadingCells = YES;
- [self mainThreadPerform:_updateCells wait:NO];
- }
-
- return iCell;
- }
-
- /* return current number of image cells */
- - (int)imageListCount
- {
- int cnt;
- mutex_lock(loadMutex);
- cnt = [imageList count];
- mutex_unlock(loadMutex);
- return cnt;
- }
-
- /* return indexed image cell */
- - imageAt:(int)index
- {
- id iCell;
- mutex_lock(loadMutex);
- iCell = [imageList objectAt:index];
- mutex_unlock(loadMutex);
- return iCell;
- }
-
- /* add image cell */
- - addImageCell:iCell
- {
- mutex_lock(loadMutex);
- [imageList addObject:iCell];
- mutex_unlock(loadMutex);
- return iCell;
- }
-
- // -------------------------------------------------------------------------------------
- // cell selection
-
- /* drag image off of window */
- - _dragImage:(NXPoint*)mousePt:(NXEvent*)e
- {
- NXPoint pt = *mousePt;
- int row, col;
- id cellId;
- NXRect rect = { { 0.0, 0.0 }, {48.0, 48.0 } };
-
- /* get cell/row/col */
- cellId = [self getRow:&row andCol:&col forPoint:&pt];
- if (!cellId || ![cellId imagePath]) return self;
-
- /* message window server for drag */
- rect.origin.x = pt.x - rect.size.width / 2.0;
- rect.origin.y = pt.y - rect.size.height / 2.0;
- [delegate _unregisterWindow];
- [self dragFile:[cellId imagePath] fromRect:&rect slideBack:YES event:e];
- [delegate _registerWindow];
-
- return self;
- }
-
- /* drag cell around matrix */
- #define DRAGMASK (NX_MOUSEDRAGGEDMASK | NX_LMOUSEUPMASK)
- - _dragCell:(NXPoint*)mousePt:(NXEvent*)e
- {
- NXPoint pt = *mousePt, lastPt;
- int oldMask, row, col;
- float minX, maxX, minY, maxY;
- NXSize half;
- NXRect viewFrame, cellFrame, lastFrame, firstFrame, drawFrame;
- id cellId, tempView, cellCache;
-
- /* get/select cell */
- [self clearSelectedCell];
- cellId = [self getRow:&row andCol:&col forPoint:&pt];
- if (!cellId || ![cellId imagePath]) return self;
- [self selectCellAt:row:col];
- [self display];
-
- /* setup mask for dragging */
- oldMask = [window addToEventMask:DRAGMASK];
-
- /* make local image copy of cell */
- [self getCellFrame:&cellFrame at:row:col];
- viewFrame = cellFrame;
- viewFrame.X = viewFrame.Y = 0.0;
- half.width = floor(viewFrame.W / 2.0);
- half.height = floor(viewFrame.H / 2.0);
- cellCache = [[NXImage alloc] initSize:&viewFrame.size];
- [cellCache setDataRetained:YES];
- [cellCache setFlipped:YES];
- [cellCache lockFocus];
- tempView = [[[View alloc] initFrame:&viewFrame] setFlipped:YES];
- [cellId drawSelf:&viewFrame inView:[[window contentView] addSubview:tempView]];
- [tempView free];
- [cellCache unlockFocus];
-
- /* set cell drag mode */
- [cellId setDragMode:YES];
- [self display];
-
- /* get drag boundaries */
- [self getCellFrame:&firstFrame at:0:0];
- minX = firstFrame.X - half.width;
- minY = firstFrame.Y + firstFrame.H - half.height;
- [self getCellFrame:&lastFrame at:(numRows-1):(numCols-1)];
- maxX = lastFrame.X + lastFrame.W - half.width;
- maxY = lastFrame.Y + lastFrame.H + half.height;
-
- /* drag cell image */
- [self lockFocus];
- lastPt.x = cellFrame.X;
- lastPt.y = cellFrame.Y + viewFrame.H;
- drawFrame = cellFrame;
- [cellCache composite:NX_COPY toPoint:&lastPt];
- [window flushWindow];
- for (;;) {
- NXEvent *ev = [NXApp getNextEvent:DRAGMASK], peek;
- while ([NXApp peekNextEvent:DRAGMASK into:&peek]) ev = [NXApp getNextEvent:DRAGMASK];
- [self autoscroll:ev];
- lastPt = ev->location;
- [self convertPoint:&lastPt fromView:(id)nil];
- lastPt.x = (numCols > 1)? (lastPt.x - half.width) : 0.0;
- lastPt.y += half.height;
- if (lastPt.x < minX) lastPt.x = minX;
- if (lastPt.x > maxX) lastPt.x = maxX;
- if (lastPt.y < minY) lastPt.y = minY;
- if (lastPt.y > maxY) lastPt.y = maxY;
- if (ev->type == NX_LMOUSEUP) break;
- cellFrame.X = lastPt.x;
- cellFrame.Y = lastPt.y - viewFrame.H;
- NXUnionRect(&cellFrame, &drawFrame);
- PSgsave();
- NXRectClip(&drawFrame);
- [self drawSelf:&drawFrame : 1];
- [cellCache composite:NX_COPY toPoint:&lastPt];
- [window flushWindow];
- PSgrestore();
- drawFrame = cellFrame;
- }
- [self unlockFocus];
- [window setEventMask:oldMask];
- [cellId setDragMode:NO];
- [cellCache free];
-
- /* find insert point */
- NXPoint targetPt = lastPt;
- targetPt.x += viewFrame.W;
- if (numCols > 1) targetPt.y -= half.height;
- int nRow = (int)floor((targetPt.y + intercell.height) / (viewFrame.H + intercell.height));
- int nCol = (int)floor((targetPt.x + intercell.width ) / (viewFrame.W + intercell.width ));
- if (nRow < 0) nRow = 0;
- if ((nCol < 0) || (numCols == 1)) nCol = 0;
-
- /* re-arrange cells */
- if ((row != nRow) || (col != nCol)) {
-
- /* find end of valid cells */
- int oldNdx = (row * numCols) + col;
- int newNdx = (nRow * numCols) + nCol;
- if (newNdx > (int)[cellList count]) newNdx = [cellList count];
- for (;(newNdx > 0) && ![[cellList objectAt:newNdx - 1] image]; newNdx--);
-
- /* move cell */
- if (newNdx != (oldNdx + 1)) {
- [imageList insertObject:self at:newNdx]; // use 'self' as placeholder
- [imageList removeObject:cellId];
- [imageList replaceObject:self with:cellId];
- [self sizeToCells]; // rebuild/refresh 'cellList'
- }
-
- }
-
- /* redisplay matrix */
- [self selectCell:cellId];
- [self display];
-
- /* return */
- return self;
-
- }
- #undef DRAGMASK
-
- /* intercept mouse down event */
- - mouseDown:(NXEvent*)e
- {
-
- /* ignore while images are being loaded into palette */
- if ([delegate isLoading]) return self;
-
- /* convert point to view */
- NXPoint pt = e->location;
- [self convertPoint:&pt fromView:(id)nil];
-
- /* Alternate-Shift */
- if ((e->flags) & NX_ALTERNATEMASK) return [self _dragImage:&pt:e];
-
- /* Command-Shift */
- if ((e->flags) & NX_COMMANDMASK) return [self _dragCell:&pt:e];
-
- /* return regular mouse-down event */
- return [super mouseDown:e];
-
- }
-
- /* return true if there are any selectable cells */
- - (BOOL)anySelectableCells
- {
- return [self imageListCount]? YES : NO;
- }
-
- /* find image with name */
- - findCellWithImageFilePath:(char*)p
- {
- int i;
- id o, f = (id)nil;
- i = [self imageListCount];
- while(i) if (!strcmp([(o=[self imageAt:--i]) imagePath], p)) { f = o; break; }
- return f;
- }
-
- /* return list of selected cells */
- - (int)selectedCellCount
- {
- int c = 0, i;
- i = [self imageListCount];
- while(i) if ([[self imageAt:--i] isSelected]) c++;
- return c;
- }
-
- /* return first selected cell found */
- - selectedCell
- {
- int i;
- id c, r = (id)nil;
- i = [self imageListCount];
- while(i) if ([(c = [self imageAt:--i]) isSelected]) r = c;
- return r;
- }
-
- /* return list of selected cells */
- - selectedCellList
- {
- int i;
- id c, list = listALLOC(2);
- i = [self imageListCount];
- while(i) if ([(c=[self imageAt:--i]) isSelected]) [list addObject:c];
- if (![list count]) { [list free]; list = (id)nil; }
- return list;
- }
-
- /* return string containing file names of all selected cells */
- - (char*)selectedCellPaths
- {
- id list = [self selectedCellList];
- char *fullPath = [self imagePaths:list];
- [list free];
- return fullPath;
- }
-
- // -------------------------------------------------------------------------------------
- // sort compare methods
-
- /* sort/compare by image name */
- - (int)compareCellTitle:image1:image2
- {
- return strcasecmp([image1 cellTitle], [image2 cellTitle]);
- }
-
- // -------------------------------------------------------------------------------------
- // sort images
-
- /* indexed from 1 */
- #define _COMPARE(I, J) ((int)[self perform:sortCompare with:(I) with:(J)])
- #define _ELEM(i) [imageList objectAt:(i) - 1]
- #define _setELEM(i, V) [imageList replaceObjectAt:(i) - 1 with:(V)]
-
- /* shell sort: O(N ^ 1.5) */
- - shellSort:(SEL)sortCompare
- {
- int i, j, h, N;
- id elem, temp;
-
- /* check for invalid compare method */
- if (!sortCompare) return (id)nil;
-
- /* lock imageList */
- mutex_lock(loadMutex);
-
- /* determine h: (1 + 3 + 3^2 + 3^3 + 3^4 + ...) [ h = pow(3,floor(log(2*N+1)/log(3))) ]*/
- for (N = [imageList count], h = 1; h <= N; h = 3 * h + 1);
-
- /* shell sort */
- while (h > 1)
- for (h /= 3, i = h + 1; i <= N; i++) {
- elem = _ELEM(i);
- for (j=i; (j>h) && (_COMPARE((temp=_ELEM(j-h)),elem)>0); j-=h) _setELEM(j, temp);
- _setELEM(j, elem);
- }
-
- /* unlock imageList */
- mutex_unlock(loadMutex);
-
- return self;
- }
-
- @end
-